package com.jcai.middlend.codegenerator.processor;

import com.google.auto.service.AutoService;
import com.jcai.middlend.codegenerator.annotation.ExceptionAdvice;
import com.jcai.middlend.codegenerator.annotation.ResponseStatus;
import com.squareup.javapoet.*;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;


import javax.annotation.Resource;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import javax.lang.model.util.Elements;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Collections;
import java.util.Set;

@AutoService(Processor.class)
public class ExceptionAdviceHandler extends AbstractProcessor {


  private Filer filer;
  private Messager messager;
  private Elements elementUtils;
  public static final String doc = "\n This codes are generated automatically. Do not modify! \n -.- \n created by zhuCan \n";



  @Override
  public synchronized void init(ProcessingEnvironment processingEnvironment) {
    super.init(processingEnvironment);
    filer = processingEnv.getFiler();
    messager = processingEnvironment.getMessager();
    elementUtils = processingEnvironment.getElementUtils();
  }

  @Override
  public Set<String> getSupportedAnnotationTypes() {
    return Collections.singleton(ExceptionAdvice.class.getCanonicalName());
  }

  @Override
  public SourceVersion getSupportedSourceVersion() {
    return SourceVersion.latestSupported();
  }

  @Override
  public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
    System.err.println("Processor : " + getClass().getSimpleName());
    long start = System.currentTimeMillis();

    Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(ExceptionAdvice.class);

    elements.forEach(x -> {
      PackageElement packageElement = elementUtils.getPackageOf(x);
      String packageName = packageElement.getQualifiedName().toString();
      // 被扫描的类名
      Name simpleName = x.getSimpleName();

      ResponseStatus status = x.getAnnotation(ResponseStatus.class);

      // 拦截异常的 方法注解
      AnnotationSpec exceptionHandler = AnnotationSpec.builder(ExceptionHandler.class).addMember("value", CodeBlock.builder().add("$T.class", x).build()).build();
      AnnotationSpec responseStatus = AnnotationSpec.builder(ResponseStatus.class).addMember("value", CodeBlock.builder().add("$T." + status.value().name(), HttpStatus.class).build()).build();


      // 拦截异常处理器
      MethodSpec methodSpec = null;
      try {
        methodSpec = MethodSpec.methodBuilder(simpleName + "Handler")
            .addModifiers(Modifier.PUBLIC)
            .returns(ParameterizedTypeName.get(ClassName.get(Resource.class),
                ClassName.get(ErrorDTO.class)))
            .addJavadoc(" handler method \n")
            .addAnnotation(exceptionHandler)
            .addAnnotation(responseStatus)
            .addParameter(HttpServletRequest.class, "req")
            .addParameter(Class.forName(packageName + "." + simpleName), "ex")
            .addStatement("$T errorDTO = new $T(ex.getMessage(), $T." + status.value().name() + ".value(), req.getRequestURL().toString())", ErrorDTO.class, ErrorDTO.class, HttpStatus.class)
            .addStatement("return new $T<$T>(errorDTO)", Resource.class, ErrorDTO.class)
            .build();
      } catch (ClassNotFoundException e) {
        e.printStackTrace();
      }


      // 构建类
      TypeSpec clazz = TypeSpec.classBuilder(x.getSimpleName() + "Advice")
          .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
          .addAnnotation(RestControllerAdvice.class)
          .addJavadoc(" generator for exception advice !" + doc)
          .addMethod(methodSpec)
          .build();
      try {
        // 创建java文件
        JavaFile javaFile = JavaFile.builder(packageName, clazz)
            .build();
        // 写入
        javaFile.writeTo(filer);
      } catch (IOException e) {
        e.printStackTrace();
      }

    });
    System.out.println("耗时 : " + (System.currentTimeMillis() - start) + " 毫秒");
    return true;
  }
}
